<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>面向对象思考与 golang 编程 - 服务计算 - 中山大学</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">


<meta name="author" content="潘茂林" /><meta name="description" content="面向对象思考与 golang cobra 库实现原理 {:.no_toc} 目录 {:toc} golang 不是面向对象的语言 ，在golang中函数是一类成员（First-class function）/知乎解释" /><meta name="keywords" content="中山大学, 数据科学与计算机, 服务计算" />






<meta name="generator" content="Hugo 0.74.3 with theme even" />


<link rel="canonical" href="http://pmlpml.git.io/service-computing/post/oo-thinking/" />
<link rel="apple-touch-icon" sizes="180x180" href="/service-computing/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/service-computing/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/service-computing/favicon-16x16.png">
<link rel="manifest" href="/service-computing/manifest.json">
<link rel="mask-icon" href="/service-computing/safari-pinned-tab.svg" color="#5bbad5">



<link href="/service-computing/sass/main.min.8c3cbcb0324c2bb4875ceccba4007cbad4b4ac8377f33af9953c3e7684534a50.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">


<meta property="og:title" content="面向对象思考与 golang 编程" />
<meta property="og:description" content="面向对象思考与 golang cobra 库实现原理 {:.no_toc} 目录 {:toc} golang 不是面向对象的语言 ，在golang中函数是一类成员（First-class function）/知乎解释" />
<meta property="og:type" content="article" />
<meta property="og:url" content="http://pmlpml.git.io/service-computing/post/oo-thinking/" />

<meta itemprop="name" content="面向对象思考与 golang 编程">
<meta itemprop="description" content="面向对象思考与 golang cobra 库实现原理 {:.no_toc} 目录 {:toc} golang 不是面向对象的语言 ，在golang中函数是一类成员（First-class function）/知乎解释">

<meta itemprop="wordCount" content="5599">



<meta itemprop="keywords" content="" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="面向对象思考与 golang 编程"/>
<meta name="twitter:description" content="面向对象思考与 golang cobra 库实现原理 {:.no_toc} 目录 {:toc} golang 不是面向对象的语言 ，在golang中函数是一类成员（First-class function）/知乎解释"/>

<!--[if lte IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
<![endif]-->

<!--[if lt IE 9]>
  <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
<![endif]-->

</head>
<body>
  <div id="mobile-navbar" class="mobile-navbar">
  <div class="mobile-header-logo">
    <a href="/service-computing/" class="logo">服务计算</a>
  </div>
  <div class="mobile-navbar-icon">
    <span></span>
    <span></span>
    <span></span>
  </div>
</div>
<nav id="mobile-menu" class="mobile-menu slideout-menu">
  <ul class="mobile-menu-list">
    <a href="/service-computing/post/index-2020">
        <li class="mobile-menu-item">首页</li>
      </a><a href="/service-computing/post/">
        <li class="mobile-menu-item">归档</li>
      </a><a href="/service-computing/tags/">
        <li class="mobile-menu-item">标签</li>
      </a><a href="/service-computing/about/">
        <li class="mobile-menu-item">关于</li>
      </a>
  </ul>
</nav>
  <div class="container" id="mobile-panel">
    <header id="header" class="header">
        <div class="logo-wrapper">
  <a href="/service-computing/" class="logo">服务计算</a>
</div>

<nav class="site-navbar">
  <ul id="menu" class="menu">
    <li class="menu-item">
        <a class="menu-item-link" href="/service-computing/post/index-2020">首页</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/post/">归档</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/tags/">标签</a>
      </li><li class="menu-item">
        <a class="menu-item-link" href="/service-computing/about/">关于</a>
      </li>
  </ul>
</nav>
    </header>

    <main id="main" class="main">
      <div class="content-wrapper">
        <div id="content" class="content">
          <article class="post">
    
    <header class="post-header">
      <h1 class="post-title">面向对象思考与 golang 编程</h1>

      <div class="post-meta">
        <span class="post-time"> 0001-01-01 </span>
        
        
      </div>
    </header>

    <div class="post-toc" id="post-toc">
  <h2 class="post-toc-title"></h2>
  <div class="post-toc-content always-active">
    <nav id="TableOfContents">
  <ul>
    <li><a href="#面向对象思考与-golang-cobra-库实现原理">面向对象思考与 golang cobra 库实现原理</a>
      <ul>
        <li><a href="#环境准备">环境准备</a></li>
        <li><a href="#1面向对象设计与编程基本概念">1、面向对象设计与编程基本概念</a></li>
        <li><a href="#2golang-的语言知识与折中trade-off">2、Golang 的语言知识与折中（trade off）</a>
          <ul>
            <li><a href="#21-go-语言的基本元素">2.1 Go 语言的基本元素</a></li>
            <li><a href="#22-go-的包装与隐藏">2.2 Go 的包装与隐藏</a></li>
            <li><a href="#23-数据抽象">2.3 数据抽象</a></li>
            <li><a href="#24-匿名组合-pk-继承">2.4 匿名组合 pk “继承”</a></li>
            <li><a href="#25-多态">2.5 多态</a></li>
          </ul>
        </li>
        <li><a href="#3设计模式应用---command-对象设计">3、设计模式应用 - Command 对象设计</a>
          <ul>
            <li><a href="#31-单实例singleton模式">3.1 单实例（Singleton）模式</a></li>
            <li><a href="#32-命令command模式">3.2 命令（Command）模式</a></li>
            <li><a href="#33-模板方法template-methods模式">3.3 模板方法（Template methods）模式</a></li>
            <li><a href="#34-组合composite模式">3.4 组合（Composite）模式</a></li>
          </ul>
        </li>
        <li><a href="#4小结">4、小结</a></li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
    <div class="post-content">
      <h1 id="面向对象思考与-golang-cobra-库实现原理">面向对象思考与 golang cobra 库实现原理</h1>
<p>{:.no_toc}</p>
<ul>
<li>目录
{:toc}</li>
</ul>
<p><strong>golang 不是面向对象的语言</strong> ，在golang中函数是一类成员（<a href="https://en.wikipedia.org/wiki/First-class_function">First-class function</a>）/<a href="https://www.zhihu.com/question/27460623">知乎解释</a> 。本文不打算纠结 golang 有哪些面向对象特性，仅关注面向对象的思想如何在 golang 中应用，让我们轻松一些写出类似 <a href="https://github.com/spf13/cobra">cobra</a> 中 comamnd.go 这样易于使用、可扩展的程序。</p>
<blockquote>
<p>本文要点：</p>
<ol>
<li>面向对象设计与编程基本概念</li>
<li>Golang 的与面向对象支持相关的知识</li>
<li>用设计模式设计 command.go</li>
</ol>
<p>前提条件：</p>
<ol>
<li>go tour 练习完成</li>
<li>使用 flag 包处理简单的 cli</li>
<li>熟悉 C++ 或 Java</li>
</ol>
</blockquote>
<h2 id="环境准备">环境准备</h2>
<p><a href="https://github.com/spf13/cobra">cobra</a> 是一个构建命令行应用接口的工具，使用 <code>go get github.com/spf13/cobra</code>。 使用 cobra 命令创建一个 golang 应用，文件结构</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">main.go
/cmd
    root.go
    register.go
    delete.go
</code></pre></td></tr></table>
</div>
</div><p>其中： register 和 delete 命令支持 <code>-u --user=name</code> 参数。</p>
<p>阅读corba文档代码示例和生成的文件，它提供了解耦合良好的程序模块，每个命令拥有自己独立的参数定义和执行逻辑可包装在一个独立的 go 源代码文件中。</p>
<p><strong>问题： Command 为什么要这样设计？</strong>，其实它一个经典面向对象设计模式使用的技巧！</p>
<h2 id="1面向对象设计与编程基本概念">1、面向对象设计与编程基本概念</h2>
<p><strong>什么是面向对象？</strong></p>
<blockquote>
<p><strong>Everything is an Object.</strong></p>
<div align = right>--- Bruce Eckel 《Thinking Java》</div>
</blockquote>
<p>对于普通人，面向对象设计与编程是最常见的选择。多年产业实践证明，面向对象具有具有易于理解、易于复用（reuse）和可扩展（extend）的优势。如果我们把世界的一切用函数来理解，这需要你具备更加优秀的抽象思维能力，特别是数学思维能力。Lisp 等语言的成功，证明了以 λ 演算为基础语言的重要性，它更容易高效编写高品质的程序。同样，用顺序、分支与循环这样结构化方法理解计算，则相对机械一些。</p>
<p><strong>面向对象的语言？</strong></p>
<p>以 <strong>对象</strong> 作为基本 <strong>程序结构单位</strong> 的程序设计语言。纯面向对象语言：Smalltalk, Eiffel(埃菲尔)，&hellip;，Java。 其中 Java 是最成功的程序设计语言，长期排在 <a href="https://www.tiobe.com/tiobe-index/">TIOBE 编程语言指数</a>排行榜前二位。随着互联网发展，尽管出现了许多新的语言 Golang, Clojure, Scala，Dart 等更挣钱的语言的竞争，Java 目前稳居排行榜第一位， “存在即真理” 的背后，一定有它的道理。</p>
<p><strong>什么是对象</strong></p>
<blockquote>
<p>An object is <strong>the simulation of an entity in a computer</strong>.<br>
An object is <strong>an encapsulation of attributes, behaviors and constraints</strong>.</p>
</blockquote>
<p><img src="images/oo-world.png" alt="对象与现实"></p>
<p><strong>面向对象语言的特点</strong></p>
<ul>
<li>包装 Encapsulation</li>
<li>信息隐藏 Information Hiding</li>
<li>数据抽象 Data Abstraction</li>
<li>继承 Inheritance</li>
<li>多态 Polymorphism</li>
</ul>
<p>通过上述技术，实现了 Reference System -&gt; Design &amp; Programming</p>
<p><strong>面向对象的设计</strong></p>
<p>设计对象、接口、以及对象之间的关系，三个元素用于求解问题。</p>
<p><strong>设计模式</strong></p>
<p>常见应用场景的涉及的对象、对象外部特征、及其之间关系，以及典型代码。例如：</p>
<ul>
<li>单例（Singleton）模式</li>
<li>命令（Command） 模式</li>
<li>模板方法（Template methods）模式</li>
<li>组合（Composite）模式</li>
<li>&hellip;</li>
</ul>
<h2 id="2golang-的语言知识与折中trade-off">2、Golang 的语言知识与折中（trade off）</h2>
<p>函数虽然高雅，但不是一般人容易理解与接受，并写出高品质的程序。运用面向对象的思想在非 OOP 语言中编程，就是必须掌握的技能了。
首先，我们复习一些语言知识，同时讨论 go 语言设计与折中的选择。</p>
<h3 id="21-go-语言的基本元素">2.1 Go 语言的基本元素</h3>
<ul>
<li>数据与数据类型 type</li>
<li>函数 func</li>
<li>包 package</li>
</ul>
<p>深入理解 <a href="http://www.admin5.com/article/20061113/6304.shtml">Nicklaus Wirth</a> <strong>算法+数据结构=程序</strong> 这句话永远不错。</p>
<p>Go的理念： 简单、简单、简单  （没考证 ！？对吗？）</p>
<ul>
<li>数据就是数据，是不可变的。例如：
<ul>
<li>Student 作为数据就是 Student，不是 Person</li>
<li>Student 作为对象是 Person</li>
</ul>
</li>
<li>函数是一种类型，值是 First-Class 的
<ul>
<li>函数类型 = 函数签名 (什么是函数签名？)</li>
</ul>
</li>
</ul>
<p>Go 是静态类型化的。<strong>每个变量都有一个静态类型，也就是说，在编译的时候变量的类型就被很精确地确定下来。</strong></p>
<h3 id="22-go-的包装与隐藏">2.2 Go 的包装与隐藏</h3>
<p><strong>包导出类型与数据</strong></p>
<p>go 的包装单位是 package。 将包中数据或类型的 <strong>第一个字母大写</strong> ，就导出该包中的内容。</p>
<p><strong>包命名</strong></p>
<p>包的全名称是工作区 src 中目录结构的路径（除了语言内置包），短名称就是最后目录名。为了便于编程，包可以使用 <strong>别名(alias)</strong>
例如，我们不喜欢 go 内置包 <code>flag</code> 处理参数的风格，我们喜欢 POSIX 风格命令行，怎么办？</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kn">import</span> <span class="p">(</span>
  <span class="nx">flag</span> <span class="s">&#34;github.com/spf13/pflag&#34;</span>
<span class="p">)</span>
</code></pre></td></tr></table>
</div>
</div><p>搞定啦！你命令行程序不需要修改其他地方了。</p>
<p><em>实践</em>： 将 delete.go 程序拖到 main.go 所在的目录，运行程序 <code>go run main.go help</code> 结果是？</p>
<p><strong>go 程序文件</strong></p>
<p>包由一些 go 文件构成。一个包中不能出现相同的类型或变量，包括函数名。它不似 java 那样一个文件对应一个对象类型，你必须机智的记住 go 文件中定义的包变量和类型。你可能需要编写 <code>vars.go</code> 或 <code>types.go</code> 来集中管理它们，但一个类型、一个变量一个文件也是最佳实践之一。</p>
<p><strong>init() 和 main() 函数</strong></p>
<p>每个 go 文件可以有一个特殊的函数 <code>init()</code>， 它是包中唯一可以重名的函数。包初始化代码必须在 init() 中。</p>
<p>它们的执行顺序是？ 参见 <a href="https://studygolang.com/articles/5036">go/golang main() init()方法的调用</a></p>
<h3 id="23-数据抽象">2.3 数据抽象</h3>
<p><em>基本类型不用解释！</em></p>
<p>Java 数据抽象仅一类： Object， 它对外呈现一些 interface，对象之间通过消息协作。</p>
<p>Go 要复杂一些：</p>
<ul>
<li>struct</li>
<li>func</li>
<li>interface</li>
<li>specials: chan</li>
</ul>
<p><strong>methods</strong></p>
<p>go 的方法似乎很有特色，让我们操作数据时有了“面向对象”的感觉。其实，在上世纪面向对象语言<a href="https://en.wikipedia.org/wiki/Eiffel_(programming_language)">Eiffel(埃菲尔)</a> （第二个）就有了啊。感兴趣去<a href="https://en.wikipedia.org/wiki/Eiffel_(programming_language)#Operator_and_bracket_syntax.2C_assigner_commands">看一看</a>。</p>
<ul>
<li>var type 语法</li>
<li>eiffelThreads vs goroutine 并发</li>
<li>static typing</li>
<li>&hellip;</li>
</ul>
<p>go-method 的确是值得夸赞的创新，它与接口实现了无缝对接。它通过 <code>receiver argument</code> 概念，让不同类型的 operator 具有了相同的函数签名。而一组函数签名相同的操作与接口一致，则实现了接口与数据类型的隐式静态绑定。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Vertex</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">X</span><span class="p">,</span> <span class="nx">Y</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">v</span> <span class="nx">Vertex</span><span class="p">)</span> <span class="nf">Abs</span><span class="p">()</span> <span class="kt">float64</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="o">*</span><span class="nx">v</span><span class="p">.</span><span class="nx">X</span> <span class="o">+</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="o">*</span><span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">Abs</span><span class="p">(</span><span class="nx">v</span> <span class="nx">Vertex</span><span class="p">)</span> <span class="kt">float64</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">math</span><span class="p">.</span><span class="nf">Sqrt</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="o">*</span><span class="nx">v</span><span class="p">.</span><span class="nx">X</span> <span class="o">+</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="o">*</span><span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">v</span> <span class="o">:=</span> <span class="nx">Vertex</span><span class="p">{</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">}</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nf">Abs</span><span class="p">(),</span> <span class="nf">Abs</span><span class="p">(</span><span class="nx">v</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>在以上代码中，<code>函数 Abs</code> 与 <code>方法 Abs</code> 语义是一样的，方法可以被认为是“语法糖”。但对于编译，Vertex 类型的数据可 <strong>静态</strong> 推导到接口类型</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Abser</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Abs</span><span class="p">()</span> <span class="kt">float64</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><strong>构造方法</strong></p>
<p>为了实现 struct 的构造，golang 提供了一些编程约定（convention），例如：</p>
<ul>
<li>提供 <code>NewVertex(v Vertex) *Vertex</code> 这样的函数</li>
<li>提供 <code>(v *Vertex) New()</code> 这样的方法</li>
</ul>
<p><strong>接口抽象</strong></p>
<p>这种静态绑定，为 go 处理多种数据带来了便利。因为任何类型数据都一个匹配到空接口 <code>interface{}</code></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Println formats using the default formats for its operands and writes to standard output.
</span><span class="c1">// Spaces are always added between operands and a newline is appended.
</span><span class="c1">// It returns the number of bytes written and any write error encountered.
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">Println</span><span class="p">(</span><span class="nx">a</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stdout</span><span class="p">,</span> <span class="nx">a</span><span class="o">...</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这样 Println 就可以打印所有类型的数据。关键是 fmt 如何从 <code>interface{}</code> 得到原数据和 <code>Stringer</code> 接口</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Stringer is implemented by any value that has a String method,
</span><span class="c1">// which defines the ``native&#39;&#39; format for that value.
</span><span class="c1">// The String method is used to print values passed as an operand
</span><span class="c1">// to any format that accepts a string or to an unformatted printer
</span><span class="c1">// such as Print.
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Stringer</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">String</span><span class="p">()</span> <span class="kt">string</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>它与 Go 内部实现机制，通过反射技术 <a href="https://golang.top/articles/2157">Go语言中反射包的实现原理</a> 可见一般。</p>
<p>阅读 go 的语言库源代码，如 fmt.Println。这是最快、最强大的学习方法！</p>
<h3 id="24-匿名组合-pk-继承">2.4 匿名组合 pk “继承”</h3>
<p>提到继承，总有许多争议。例如，继承的副作用？</p>
<ul>
<li>继承是白盒的，这导致一个类继承了基类，基类与子类必须都进行完整的测试。</li>
<li>多重继承的复杂性。</li>
<li>继承与组合。哪些条件不能使用继承？例如：
<ul>
<li>班长一般不能继承于学生</li>
<li>方形不能继承于长方形</li>
</ul>
</li>
</ul>
<p>Golang 不支持继承，但很好的支持组合，因为“<strong>组合优于继承</strong>”<a href="https://www.zhihu.com/question/21862257">知乎的一些讨论</a>（萧萧是正确的）。但提供了类似“继承”的语法。</p>
<p>例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Vertex</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">X</span><span class="p">,</span> <span class="nx">Y</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Circle</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">Vertex</span>
  <span class="nx">R</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Cylinder</span> <span class="kd">struct</span> <span class="p">{</span>
  <span class="nx">Circle</span>
  <span class="nx">H</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">ToCircle</span><span class="p">(</span><span class="nx">u</span> <span class="o">*</span><span class="nx">Vertex</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">Cylinder</span><span class="p">)</span> <span class="o">*</span><span class="nx">Circle</span> <span class="p">{</span>
	<span class="k">if</span> <span class="o">&amp;</span><span class="nx">r</span><span class="p">.</span><span class="nx">Vertex</span> <span class="o">==</span> <span class="nx">u</span> <span class="p">{</span>
		<span class="k">return</span> <span class="o">&amp;</span><span class="nx">r</span><span class="p">.</span><span class="nx">Circle</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">cy</span> <span class="o">:=</span> <span class="nx">Cylinder</span><span class="p">{</span><span class="nx">Circle</span><span class="p">{},</span> <span class="mi">10</span><span class="p">}</span>
	<span class="c1">//cy := Cylinder{X: 3} 编译错误
</span><span class="c1"></span>	<span class="nx">cy</span><span class="p">.</span><span class="nx">X</span> <span class="p">=</span> <span class="mi">3</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cy</span><span class="p">)</span>
	<span class="nx">v</span> <span class="o">:=</span> <span class="nx">cy</span><span class="p">.</span><span class="nx">Vertex</span>
	<span class="nx">v</span><span class="p">.</span><span class="nx">Y</span> <span class="p">=</span> <span class="mi">4</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cy</span><span class="p">,</span> <span class="nx">v</span><span class="p">)</span> <span class="c1">//cy = ?
</span><span class="c1"></span>	<span class="nx">u</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="p">(</span><span class="nx">cy</span><span class="p">.</span><span class="nx">Vertex</span><span class="p">)</span>
	<span class="nx">u</span><span class="p">.</span><span class="nx">Y</span> <span class="p">=</span> <span class="mi">4</span>
	<span class="c1">//c := Circle(u) 编译错误
</span><span class="c1"></span>	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cy</span><span class="p">,</span> <span class="nx">u</span><span class="p">)</span> <span class="c1">//cy = ?
</span><span class="c1"></span>	<span class="nx">c</span> <span class="o">:=</span> <span class="nf">ToCircle</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">cy</span><span class="p">)</span>
	<span class="nx">c</span><span class="p">.</span><span class="nx">R</span> <span class="p">=</span> <span class="mi">5</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">cy</span><span class="p">,</span> <span class="nx">c</span><span class="p">,</span> <span class="nx">c</span><span class="p">.</span><span class="nx">Vertex</span><span class="p">)</span> <span class="c1">//cy = ?
</span><span class="c1"></span><span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>上述程序很有趣，cy 是 Cylinder 类型的数据。</p>
<ul>
<li><code>Cylinder{X: 3}</code> 为什么错误， <code>cy.X = 3</code> 为什么正确，这语法玩的溜！</li>
<li><code>v := cy.Vertex</code> v 是值，不是指针。 似乎是 deeply clone ?</li>
<li>别指望面向对象哪种向上类型转换（UpCasting），注意用指针哦（操）！</li>
<li>别指望面向对象哪种向下类型转换（DownCasting），自己手工实现。</li>
<li><code>c := ToCircle(u, &amp;cy)</code> 改为 <code>c := ToCircle(&amp;v, &amp;cy)</code> 结果是？</li>
</ul>
<p>没有面向对象，以前系统运行时（runtime）做的一切，都需要自己亲手实现。
不过习惯静态类型就好了，唯一的麻烦就是 值与指针从变量名太难区分！</p>
<p><em>convention</em>：用大写 <code>P</code> 作为变量名后缀表示指针？？</p>
<h3 id="25-多态">2.5 多态</h3>
<p><strong>什么是多态？</strong></p>
<blockquote>
<p>Generally speaking, a name has multiple meanings.</p>
<ul>
<li>Names of data:  association between variables and types.</li>
<li>Names of operations:  association between method names and bodies.</li>
</ul>
</blockquote>
<p>为什么要有多态，面向对象的方法的说法是便于 <strong>重用</strong> 和 <strong>扩展</strong>。</p>
<p><strong>如何实现多态？</strong></p>
<blockquote>
<ul>
<li>Data:  Implicit Type Conversion + Explicit Type Conversion</li>
<li>Operations:  Overloading  + Dynamic Binding</li>
</ul>
</blockquote>
<p><strong>实现手段：Binding</strong></p>
<blockquote>
<p>Static Binding</p>
<ul>
<li>Determine the method body associated with List.search(&hellip;) at compile-time</li>
<li>Commonly used in traditional languages such as C, Pascal, Algol 68, etc.</li>
</ul>
</blockquote>
<blockquote>
<p>Dynamic Binding</p>
<ul>
<li>Determine the associated body of List.search(&hellip;) at run-time</li>
<li>C++:  Static binding by default. Support dynamic binding.</li>
<li>Java:  Dynamic binding by default. Support static binding.</li>
</ul>
</blockquote>
<p><strong>go 语言仅支持静态绑定！！！</strong></p>
<p>编译多态能做什么？</p>
<ul>
<li>Compiler-Time Polymorphism
<ul>
<li>Overloading
<ul>
<li>Method Overloading</li>
<li>Operators Overloading
<ul>
<li>System Defined</li>
<li>User Defined</li>
</ul>
</li>
</ul>
</li>
<li>Generics</li>
</ul>
</li>
</ul>
<p>go 编译器做了什么？</p>
<ul>
<li>通过方法和函数签名实现多态</li>
<li>通过接口实现多态</li>
</ul>
<p>Go中不允许不同类型的赋值，折中的解决方案就是 interface 类型大招！ <strong>interface值</strong> 是（value,type）元组。利用接口可实现：</p>
<ul>
<li>静态隐式支持接口类型 UpCasting。 interface{} 就是万能接口</li>
<li>接口查询支持 DownCasting 到 value 或 接口。</li>
</ul>
<p><strong>接口 UpCasting</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Shaper is Inteface
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Shaper</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Area</span><span class="p">()</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Square</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">L</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">Square</span><span class="p">)</span> <span class="nf">Area</span><span class="p">()</span> <span class="kt">float64</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">L</span> <span class="o">*</span> <span class="nx">s</span><span class="p">.</span><span class="nx">L</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">Rect</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">A</span><span class="p">,</span> <span class="nx">B</span> <span class="kt">float64</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="o">*</span><span class="nx">Rect</span><span class="p">)</span> <span class="nf">Area</span><span class="p">()</span> <span class="kt">float64</span> <span class="p">{</span>
	<span class="k">return</span> <span class="nx">s</span><span class="p">.</span><span class="nx">A</span> <span class="o">*</span> <span class="nx">s</span><span class="p">.</span><span class="nx">B</span>
<span class="p">}</span>

<span class="kd">type</span> <span class="nx">A4Paper</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="nx">Rect</span>
	<span class="nx">W</span> <span class="kt">int</span>
<span class="p">}</span>

<span class="c1">// A4Paper 没有实现接口
</span><span class="c1"></span>
<span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c1">//we can assign the variable of Square to variable of Shaper(interface)
</span><span class="c1"></span>	<span class="nx">sShaper</span> <span class="o">:=</span> <span class="nf">Shaper</span><span class="p">(</span><span class="nx">Square</span><span class="p">{</span><span class="mf">4.0</span><span class="p">})</span>
	<span class="nx">rShaper</span> <span class="o">:=</span> <span class="nf">Shaper</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">A4Paper</span><span class="p">{</span><span class="nx">Rect</span><span class="p">{</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">},</span> <span class="mi">80</span><span class="p">})</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">sShaper</span><span class="p">.</span><span class="nf">Area</span><span class="p">(),</span> <span class="nx">rShaper</span><span class="p">.</span><span class="nf">Area</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>接口的 UpCasting 可以解决许多问题。例如：<code>sShaper := Shaper(Square{4.0})</code> 是编译完成的隐式变换，接口值（Square{4.0}，Square）赋值给了 sShaper, 确保了运行期调用 Square.Area()，并由 Square{4.0} 接收。这是简单而安全的实现。</p>
<p><strong>接口 DownCasting</strong></p>
<p>如何实现函数 <code>func PrintShapes(a ...interface{})</code> 函数打印输入数据的面积呢？ 关键在于 <code>interface{}</code> DownCasting <code>Shaper</code>。
Golang 提供了接口查询这个特殊的语法 <code>intefaceValue.(T)</code> 称为 <em>type assertion</em></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">PrintShapes</span><span class="p">(</span><span class="nx">a</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="p">{</span>
	<span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">iface</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">a</span> <span class="p">{</span>
		<span class="c1">//type assertion to test value implement a interface
</span><span class="c1"></span>		<span class="c1">//type assertion is only applied on interface, so we cast the s to empty interface
</span><span class="c1"></span>		<span class="k">if</span> <span class="nx">v</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">iface</span><span class="p">.(</span><span class="nx">Shaper</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
			<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">v</span><span class="p">.</span><span class="nf">Area</span><span class="p">())</span>
		<span class="p">}</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这意味类型断言 <code>t,ok := intefaceValue.(T)</code> 中 <code>t</code> 的类型是编译期可以决定，但 <code>inteface 值的 type</code> 能否 upcasting 转为 <code>T</code> 则是运行期完成的。通过运行期查类型定义表，间接实现了接口 DownCasting 的任务。</p>
<p>在 main 中调用 <code>PrintShapes(Square{4}, 18, &amp;A4Paper{Rect{4, 5}, 80})</code> 我们看到了期望的结果 <code>16 20</code>。</p>
<p>请问：<code>if v, ok := iface.(Shaper);</code> 中 <code>Shaper</code> 修改为 <code>Square</code>，<code>A4Paper</code>，<code>*A4Paper</code>，<code>Rect</code>，<code>*Rect</code> 各输出什么？</p>
<h2 id="3设计模式应用---command-对象设计">3、设计模式应用 - Command 对象设计</h2>
<p>有了匿名组合，接口类型的上下Casting，函数（签名）类型，我们可以在不使用反射的条件下，实现面向对象的设计。即面向对象的设计思想可用于 golang 编程实践。</p>
<p>本节的任务是设计并用 golang 实现类似 Cobra 的 Command。</p>
<ul>
<li>cli 涉及的设计模式</li>
<li>command.go 设计原理</li>
</ul>
<h3 id="31-单实例singleton模式">3.1 单实例（Singleton）模式</h3>
<p>单实例模式是面向对象设计最常见的模式。在命令行应用中，<code>args []String</code> 就是一个全局的，唯一的变量。单实例模式使用场景：</p>
<ul>
<li>提供全局唯一静态的对象访问</li>
<li>屏蔽复杂的初始化或对象产生逻辑</li>
</ul>
<p>对于 golang ，最简单就是“饿汉方式”方法，定义一个包变量，例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">Args</span> <span class="p">=</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Args</span>

<span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;init logic here...&#34;</span><span class="p">,</span> <span class="nx">Args</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>另一种就是使用时创建，称为“懒汉方式”，是利用函数输出数据的指针。例如：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">var</span> <span class="nx">args</span> <span class="o">*</span><span class="p">[]</span><span class="kt">string</span>
<span class="kd">var</span> <span class="nx">mu</span> <span class="nx">sync</span><span class="p">.</span><span class="nx">Mutex</span>

<span class="c1">// GetArgs *
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">GetArgs</span><span class="p">()</span> <span class="o">*</span><span class="p">[]</span><span class="kt">string</span> <span class="p">{</span>
	<span class="k">if</span> <span class="nx">args</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">mu</span><span class="p">.</span><span class="nf">Lock</span><span class="p">()</span>
		<span class="k">defer</span> <span class="nx">mu</span><span class="p">.</span><span class="nf">Unlock</span><span class="p">()</span>
		<span class="k">if</span> <span class="nx">args</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
			<span class="nx">args</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">os</span><span class="p">.</span><span class="nx">Args</span>
			<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;init logic here...&#34;</span><span class="p">,</span> <span class="o">*</span><span class="nx">args</span><span class="p">)</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="nx">args</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;use cmd.GetArgs() anywhere...&#34;</span><span class="p">,</span> <span class="nf">GetArgs</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>双重锁，解决了并发效率问题</p>
<h3 id="32-命令command模式">3.2 命令（Command）模式</h3>
<p>为了解决命令者与执行者之间的分离，通常需要接口抽象，执行者实现这个这个接口，命令者使用这个简单的接口，而不需要知道实现。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">Commander</span> <span class="kd">interface</span> <span class="p">{</span>
	<span class="nf">Execute</span><span class="p">()</span> <span class="kt">error</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>这样，定义操作 <code>Execute() error</code> 的类型，都可以使用这个接口了。</p>
<h3 id="33-模板方法template-methods模式">3.3 模板方法（Template methods）模式</h3>
<p>为了执行一个命令，我们并不知道一个命令的具体参数，如何执行。 Java 典型的设计套路是：</p>
<ul>
<li>定义接口</li>
<li>定义抽象类</li>
<li>定义实现类</li>
</ul>
<p>结合 cli 程序特点，命令解析分为三个步骤：定义参数、解析命令、执行命令。模板方法就用上了，详细参考：<a href="http://www.cnblogs.com/gaochundong/p/design_pattern_template_method.html">设计模式之美：Template Method（模板方法）</a>，关键在于：</p>
<ul>
<li>抽象操作（Primitive Operation）(must be overridden)</li>
<li>钩子操作（Hook Operation）(may be overridden)，通常提供默认实现。</li>
</ul>
<p><strong>定义抽象数据</strong></p>
<p>落实到 command.go 代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Command .
</span><span class="c1"></span><span class="kd">type</span> <span class="nx">Command</span> <span class="kd">struct</span> <span class="p">{</span>
	<span class="c1">// Use is the one-line usage message.
</span><span class="c1"></span>	<span class="nx">Use</span> <span class="kt">string</span>
	<span class="c1">// Short is the short description shown in the &#39;help&#39; output.
</span><span class="c1"></span>	<span class="nx">Short</span> <span class="kt">string</span>
	<span class="c1">// Long is the long message shown in the &#39;help &lt;this-command&gt;&#39; output.
</span><span class="c1"></span>	<span class="nx">Long</span> <span class="kt">string</span>
	<span class="c1">// SetOptions:
</span><span class="c1"></span>	<span class="nx">SetOptions</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span>
	<span class="c1">// Parse:
</span><span class="c1"></span>	<span class="nx">Parse</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span>
	<span class="c1">// Run: Typically the actual work function. Most commands will only implement this.
</span><span class="c1"></span>	<span class="nx">Run</span> <span class="kd">func</span><span class="p">(</span><span class="nx">cmd</span> <span class="o">*</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span>
<span class="p">}</span>

<span class="c1">// Execute .
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Command</span><span class="p">)</span> <span class="nf">Execute</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
	<span class="k">if</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">SetOptions</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span> <span class="nx">ok</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Error in SetOptions!&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="nx">ok</span>
	<span class="p">}</span>
	<span class="k">if</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span> <span class="nx">ok</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Error in Parsing!&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="nx">ok</span>
	<span class="p">}</span>
	<span class="nx">c</span><span class="p">.</span><span class="nf">Run</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span> <span class="nx">Args</span><span class="p">)</span>
	<span class="k">return</span> <span class="kc">nil</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>上述代码的要点：</p>
<ul>
<li>定义了三个回调函数，也就是钩子操作。没有虚方法也是有办法的！</li>
<li>定义了模板方法 <code>Execute()</code></li>
</ul>
<p><strong>定义实现</strong></p>
<p>落实到 main.go 代码：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c1">//cmd.Execute()
</span><span class="c1"></span>	<span class="kd">var</span> <span class="nx">RootCmd</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
		<span class="nx">Use</span><span class="p">:</span>   <span class="s">&#34;test&#34;</span><span class="p">,</span>
		<span class="nx">Short</span><span class="p">:</span> <span class="s">&#34;A brief description of your application&#34;</span><span class="p">,</span>
		<span class="nx">Long</span><span class="p">:</span>  <span class="s">&#34;A longer description&#34;</span><span class="p">,</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">SetOptions</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Set Options here&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">Parse</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Parse here&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">Run</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">a</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Do comamnd&#34;</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p><strong>运行成果</strong></p>
<p><code>go run main.go -uPan</code></p>
<p>结果是：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-fallback" data-lang="fallback">Set Options here
Parse here
Do comamnd
</code></pre></td></tr></table>
</div>
</div><p><strong>完善程序</strong></p>
<p>command.go 添加方法 和 局部变量</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="c1">// Flags returns the complete FlagSet that applies
</span><span class="c1">// to this command (local and persistent declared here and by all parents).
</span><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">Command</span><span class="p">)</span> <span class="nf">Flags</span><span class="p">()</span> <span class="o">*</span><span class="nx">flag</span><span class="p">.</span><span class="nx">FlagSet</span> <span class="p">{</span>
	<span class="k">if</span> <span class="nx">c</span><span class="p">.</span><span class="nx">flags</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
		<span class="nx">c</span><span class="p">.</span><span class="nx">flags</span> <span class="p">=</span> <span class="nx">flag</span><span class="p">.</span><span class="nf">NewFlagSet</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nx">Use</span><span class="p">,</span> <span class="nx">flag</span><span class="p">.</span><span class="nx">ContinueOnError</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="nx">c</span><span class="p">.</span><span class="nx">flags</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>修改 main.go</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="c1">//cmd.Execute()
</span><span class="c1"></span>	<span class="kd">var</span> <span class="nx">RootCmd</span> <span class="p">=</span> <span class="o">&amp;</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">{</span>
		<span class="nx">Use</span><span class="p">:</span>   <span class="s">&#34;test&#34;</span><span class="p">,</span>
		<span class="nx">Short</span><span class="p">:</span> <span class="s">&#34;A brief description of your application&#34;</span><span class="p">,</span>
		<span class="nx">Long</span><span class="p">:</span>  <span class="s">&#34;A longer description&#34;</span><span class="p">,</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">SetOptions</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Set Options here&#34;</span><span class="p">)</span>
		<span class="nx">c</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">StringP</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">,</span> <span class="s">&#34;u&#34;</span><span class="p">,</span> <span class="s">&#34;Anonymous&#34;</span><span class="p">,</span> <span class="s">&#34;Help message for username&#34;</span><span class="p">)</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">Parse</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Parse here&#34;</span><span class="p">)</span>
		<span class="nx">c</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">Parse</span><span class="p">(</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Args</span><span class="p">)</span>
		<span class="k">return</span> <span class="kc">nil</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nx">Run</span> <span class="p">=</span> <span class="kd">func</span><span class="p">(</span><span class="nx">c</span> <span class="o">*</span><span class="nx">cmd</span><span class="p">.</span><span class="nx">Command</span><span class="p">,</span> <span class="nx">a</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Do comamnd&#34;</span><span class="p">)</span>
		<span class="nx">username</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">c</span><span class="p">.</span><span class="nf">Flags</span><span class="p">().</span><span class="nf">GetString</span><span class="p">(</span><span class="s">&#34;user&#34;</span><span class="p">)</span>
		<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;myCommand called by &#34;</span> <span class="o">+</span> <span class="nx">username</span><span class="p">)</span>
	<span class="p">}</span>
	<span class="nx">RootCmd</span><span class="p">.</span><span class="nf">Execute</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></td></tr></table>
</div>
</div><p>现在你可以方便的实现简单命令了哦！！！</p>
<h3 id="34-组合composite模式">3.4 组合（Composite）模式</h3>
<p>组合模式的意图：</p>
<ul>
<li>将对象组合成树形结构以表示 “部分-整体” 的层次结构。</li>
<li>Composite 使得用户对于单个对象和组合对象的使用具有一致性。</li>
</ul>
<p>具体参考：<a href="http://www.cnblogs.com/gaochundong/p/design_pattern_composite.html">设计模式之美：Composite（组合）</a></p>
<p>代码 <strong>实现要点</strong> ：</p>
<ul>
<li>command 添加 AddCommand。参考 cobra 的实现</li>
<li>Execute() 方法有 bug 。客户不写钩子函数没有处理</li>
<li>需要写一个标准的 Parse 钩子实现
<ul>
<li>如果不是根命令，当前 args 是 <code>c.args := parent.args[1:]</code></li>
<li>如果没有子命令，执行 <code>c.Flags().Parse(c.args)</code></li>
<li>有子命令，没匹配上 <code>c.args[]</code> 显示 help</li>
<li>匹配子命令成功，执行 <code>c.Execute()</code></li>
</ul>
</li>
</ul>
<p>没有写实现，就交给你完成了。 cobra 主要功能 OK 了。</p>
<h2 id="4小结">4、小结</h2>
<p>与 c 相比，golang 中使用面向对象的设计思想编程是不错的。语言中编译静态能做的都做的很好，类型与其接收的方法（操作）也让你有面向对象编程的感觉。除了接口，type是静态的，不能相互赋值（支持基本类型隐式转换），但接口支持隐式静态泛化实现upcasting，接口断言支持动态downcasting。因此，可以较好实现面向对象思想设计，但是运行时动态必须你自己实现。例如：没有所谓虚方法，你需要通过回调函数实现，并仔细维护回调链，这需要你有更强大的程序设计水平。总之，golang 入门容易，但达到实用水平比较难。</p>
<p>命令行处理有标准的面向对象设计模型，它与 cobra 的 golang 设计是一致的。 本文 UML 设计如图所示：</p>
<p><img src="images/oo-command-design.png" alt="UML"></p>
<p>如果你理解并完成了上述设计，恭喜你！可以称为 golang 专业一段了。</p>

    </div>

    <div class="post-copyright">
  <p class="copyright-item">
    <span class="item-title"></span>
    <span class="item-content">潘茂林</span>
  </p>
  <p class="copyright-item">
    <span class="item-title"></span>
    <span class="item-content">
        0001-01-01
        
    </span>
  </p>
  
  
</div>
<footer class="post-footer">
      
      <nav class="post-nav">
        <a class="prev" href="/service-computing/post/oo-thinking-abstract/">
            <i class="iconfont icon-left"></i>
            <span class="prev-text nav-default">面向对象思考与 golang 编程</span>
            <span class="prev-text nav-mobile"></span>
          </a>
        <a class="next" href="/service-computing/post/ex-project/">
            <span class="next-text nav-default">项目实践</span>
            <span class="next-text nav-mobile"></span>
            <i class="iconfont icon-right"></i>
          </a>
      </nav>
    </footer>
  </article>
        </div>
        

  

  

      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="social-links">
      <a href="mailto:panml@mail.sysu.edu.cn" class="iconfont icon-email" title="email"></a>
  <a href="http://pmlpml.git.io/service-computing/index.xml" type="application/rss+xml" class="iconfont icon-rss" title="rss"></a>
</div>

<div class="copyright">
  <span class="power-by">
    %!(EXTRA string=<a class="hexo-link" href="https://gohugo.io">Hugo</a>)
  </span>
  <span class="division">|</span>
  <span class="theme-info">
     - 
    <a class="theme-link" href="https://github.com/olOwOlo/hugo-theme-even">Even</a>
  </span>

  

  <span class="copyright-year">
    &copy; 
    2018 - 
    2020
    <span class="heart">
      <i class="iconfont icon-heart"></i>
    </span>
    <span class="author">潘茂林</span>
  </span>
</div>
    </footer>

    <div class="back-to-top" id="back-to-top">
      <i class="iconfont icon-up"></i>
    </div>
  </div>
  
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>



<script type="text/javascript" src="/service-computing/js/main.min.d7b7ada643c9c1a983026e177f141f7363b4640d619caf01d8831a6718cd44ea.js"></script>








</body>
</html>
